package com.jzo2o.es.utils;

import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.ObjectUtils;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;

import java.util.List;
import java.util.stream.Collectors;

/**
 * Elasticsearch搜索响应工具类，提供搜索结果的判断、提取及转换能力
 */
public class SearchResponseUtils {

    /**
     * 判断Elasticsearch搜索响应是否成功（非空且包含有效结果）
     * @param searchResponse Elasticsearch搜索响应对象
     * @return true-响应成功且有结果；false-响应为空或无结果
     */
    public static boolean isSuccess(SearchResponse<?> searchResponse) {
        // 校验响应对象、结果集容器、实际结果集均非空
        return searchResponse != null && ObjectUtils.isNotEmpty(searchResponse.hits()) && CollUtils.isNotEmpty(searchResponse.hits().hits());
    }

    /**
     * 判断Elasticsearch搜索响应是否失败（空或无有效结果）
     * @param searchResponse Elasticsearch搜索响应对象
     * @return true-响应失败或无结果；false-响应成功且有结果
     */
    public static boolean isNotSuccess(SearchResponse<?> searchResponse) {
        return !isSuccess(searchResponse);
    }

    /**
     * 从搜索响应中提取源数据列表（无额外转换）
     * @param searchResponse Elasticsearch搜索响应对象（带泛型源数据类型）
     * @param <T> 源数据类型
     * @return 源数据列表；若响应失败则返回null
     */
    public static <T> List<T> getResponse(SearchResponse<T> searchResponse) {
        // 调用带转换器的重载方法，传入null表示不进行转换
        return getResponse(searchResponse, null);
    }

    /**
     * 从搜索响应中提取源数据列表，并支持通过转换器对数据进行额外处理
     * @param searchResponse Elasticsearch搜索响应对象（带泛型源数据类型）
     * @param convert 数据转换器（可自定义对Hit元数据和源数据的处理逻辑）
     * @param <T> 源数据类型
     * @return 处理后的源数据列表；若响应失败则返回null
     */
    public static <T> List<T> getResponse(SearchResponse<T> searchResponse, Convert<T> convert) {
        // 响应失败时直接返回null
        if (!isSuccess(searchResponse)) {
            return null;
        }
        // 无转换器时，直接提取源数据并返回
        if (convert == null) {
            return searchResponse.hits().hits().stream()
                    .map(hit -> hit.source()) // 从Hit对象中提取源数据---等同于方法引用 .map(Hit::source)
                    .collect(Collectors.toList());
        }
        // 有转换器时，先通过转换器处理数据，再返回源数据
        return searchResponse.hits().hits()
                .stream()
                .map(hit -> {
                    convert.convert(hit, hit.source()); // 传入Hit元数据和源数据进行转换处理
                    return hit.source();
                }).collect(Collectors.toList());

    }

    /**
     * 搜索结果转换器接口，用于自定义Hit元数据与源数据的关联处理逻辑
     * @param <T> 源数据类型
     */
    public static interface Convert<T> {
        /**
         * 转换方法：可将Hit中的元数据（如评分、文档ID等）赋值给源数据
         * @param hit Elasticsearch搜索结果Hit对象（包含元数据）
         * @param t 对应的源数据对象
         */
        void convert(Hit<T> hit, T t);
    }
}
